#light

type Attempt<'a> = (unit -> 'a option)

let succeed x = (fun () -> Some(x)) : Attempt<'a>
let fail      = (fun () -> None) : Attempt<'a>
let runAttempt (a:Attempt<'a>) = a()
let bind p rest = match runAttempt p with None -> fail | Some r -> (rest r)
let delay f = (fun () -> runAttempt (f ()))


type AttemptBuilder() =
    member b.Return(x) = succeed x
    member b.Bind(p,rest) = bind p rest
    member b.Delay(f) = delay f
    member b.Let(p,rest) : Attempt<'a> = rest p
let attempt = new AttemptBuilder()


let alwaysOne = attempt { return 1 }

let alwaysPair = attempt { return (1,"two") }

runAttempt alwaysOne

runAttempt alwaysPair

let failIfBig n = attempt { if n > 1000 then return! fail else return n }

runAttempt (failIfBig 999)

runAttempt (failIfBig 1001)

let failIfEitherBig (inp1,inp2) =
    attempt { let! n1 = failIfBig inp1
              let! n2 = failIfBig inp2
              return (n1,n2) }
              
runAttempt (failIfEitherBig (999,998))

runAttempt (failIfEitherBig (1003,998))

runAttempt (failIfEitherBig (999,1001))

let sumIfBothSmall (inp1,inp2) =
    attempt { let! n1 = failIfBig inp1
              let! n2 = failIfBig inp2
              let sum = n1 + n2
              return sum }
